#pragma once
#include <semaphore.h>
#include <pthread.h>
static const int gcap = 5;

template <class T>
class ring_queue
{
private:
    void P(sem_t& sem)
    {
        int n = sem_wait(&sem);
        assert(0 == n);
        (void)n;
    }
    void V(sem_t& sem)
    {
        int n = sem_post(&sem);
        assert(0 == n);
        (void)n;
    }
public:
    ring_queue(const int cap = gcap)
    :_cap(cap),_proctorStep(0),_consumerStep(0),_queue(cap)
    {
        int n = sem_init(&_dataSem,0,0);
        assert(0 == n);
        n = sem_init(&_spaceSem,0,_cap);
        assert(0 == n);
        (void)n;
        pthread_mutex_init(&_pmutex,nullptr);
        pthread_mutex_init(&_cmutex,nullptr);
    }
    //生产者生产数据
    void push(const T& in)
    {
        //使用PV原语,如果要加锁，我们应该先执行PV操作，这样可以提高效率
        //在生产者竞争这把锁之前先把空间分配好
        P(_spaceSem);
        pthread_mutex_lock(&_pmutex);
        _queue[_proctorStep++] = in;
        _proctorStep %= _cap;//保证是环形队列
        pthread_mutex_unlock(&_pmutex);
        V(_dataSem);
    }
    //消费者消费数据
    void pop(T* out)
    {
        P(_dataSem);
        pthread_mutex_lock(&_cmutex);
        *out = _queue[_consumerStep++];
        _consumerStep %= _cap;
        pthread_mutex_unlock(&_cmutex);
        V(_spaceSem);
    }
    ~ring_queue()
    {
        sem_destroy(&_dataSem);
        sem_destroy(&_spaceSem);
        pthread_mutex_destroy(&_pmutex);
        pthread_mutex_destroy(&_cmutex);
    }
private:
    vector<T> _queue; //使用数组对环形队列的模拟
    int _cap; //容量
    sem_t _dataSem; //数据资源
    sem_t _spaceSem; //空间资源
    int _proctorStep; //生产者所在的位置
    int _consumerStep; //消费者所在的位置
    pthread_mutex_t _pmutex; //多个生产者保证互斥
    pthread_mutex_t _cmutex;//多个消费者保证互斥
};